﻿#include "app_dap.h"

#include "app_crc.h"
#include "app_usb.h"
#include "dap.h"
#include "dap_config.h"
#include "printf.h"
#include "usbd_cdc.h"
#include "usbd_core.h"

/*
Version	Description

V2.1.1	Allow default clock frequency to use fast clock mode

V2.1.0	Added: UART COM Commands to support target communication via extra UART
        Added: UART Receive/Transmit Buffer Size values in the command DAP_Info
        Added: Target Board Vendor and Target Board Name strings in the command
        DAP_Info Added: Product Firmware Version string in the command DAP_Info
        Changed: String encoding in DAP_Info from ASCII to UTF-8

V2.0.0	Changed: Communication via USB bulk endpoints to achieve high-speed
        transfer rates Added: Streaming SWO via separate USB bulk endpoint
        Added: DAP_SWO_Transport extended with transport mode 2 - Send trace
        data via separate USB bulk endpoint

V1.3.0	Added: Target Board Vendor and Target Board Name strings in the command
        DAP_Info Added: Product Firmware Version string in the command DAP_Info
        Changed: String encoding in DAP_Info from ASCII to UTF-8

V1.2.0	Added: dap_swd_sequence to enable SWD multi-drop target selection
        Added: Test Domain Timer values in the commands DAP_Info, dap_transfer

V1.1.0	Added: SWO Commands to support Serial Wire Output (SWO) in UART mode
        Added: Atomic Commands support for executing time critical DAP commands

V1.0.0	Version 1.0.0 was never released; version number skipped.

V0.02	Renamed DAP_LED to DAP_HostStatus.

V0.01	Beta Release.

*/

/* 外部变量 */
extern uint8_t dap_transfer_abort; /* DAP传输终止标志位 */

char dap_uinque_serial_number[32] = "99A123456789"; /* 唯一序列号字符串 */

/* DAP包缓冲区管理 */
static volatile uint32_t request_index_i;  // Request  Index In
static volatile uint32_t request_index_o;  // Request  Index Out
static volatile uint32_t request_count_i;  // Request  Count In
static volatile uint32_t request_count_o;  // Request  Count Out
static volatile uint8_t request_idle;      // Request  Idle  Flag

static volatile uint32_t response_index_i;  // Response Index In
static volatile uint32_t response_index_o;  // Response Index Out
static volatile uint32_t response_count_i;  // Response Count In
static volatile uint32_t response_count_o;  // Response Count Out
static volatile uint8_t response_idle;      // Response Idle  Flag

static volatile __ALIGNED(4) uint8_t request_buff[DAP_PACKET_COUNT][DAP_PACKET_SIZE];
static volatile __ALIGNED(4) uint8_t response_buff[DAP_PACKET_COUNT][DAP_PACKET_SIZE];
static volatile uint16_t resp_size[DAP_PACKET_COUNT];

/**
 * @brief 计算MCU唯一序列号
 *
 */
static void DAP_GetSerialNumber(void) {
    /* 用GD32F407的唯一ID的CRC值 */
    uint32_t num = APP_CRC_Get(96 / 32, (uint32_t *)0x1FFF7A10);
    snprintf(dap_uinque_serial_number, sizeof(dap_uinque_serial_number), "99A1%08X", num);
    ULOG_INFO("dap uinque serial number: %s\r\n", dap_uinque_serial_number);
}

/**
 * @brief 开始等待接收
 *
 */
void APP_DAP_StartRead(void) {
    request_idle = 0U;
    /* 开始接收 */
    usbd_ep_start_read(0, DAP_OUT_EP, (uint8_t *)&request_buff[0][0], DAP_PACKET_SIZE);
}

/**
 * @brief DAP数据接收回调
 *
 * @param busid
 * @param ep
 * @param nbytes
 */
void APP_DAP_OutCallback(uint8_t busid, uint8_t ep, uint32_t nbytes) {
    if (request_buff[request_index_i][0] == ID_DAP_TransferAbort) {
        dap_transfer_abort = 1U;
    } else {
        request_index_i++;
        if (request_index_i == DAP_PACKET_COUNT) {
            request_index_i = 0U;
        }
        request_count_i++;
    }

    /* Preparing to receive the next data packet */
    if ((uint16_t)(request_count_i - request_count_o) != DAP_PACKET_COUNT) {
        usbd_ep_start_read(0, DAP_OUT_EP, (uint8_t *)&request_buff[request_index_i][0], DAP_PACKET_SIZE);
    } else {
        request_idle = 1U;
    }
}

/**
 * @brief DAP数据发送完毕回调
 *
 * @param busid
 * @param ep
 * @param nbytes
 */
void APP_DAP_InCallback(uint8_t busid, uint8_t ep, uint32_t nbytes) {
    if (response_count_i != response_count_o) {
        /* Sending packets in the buffer */
        usbd_ep_start_write(0, DAP_IN_EP, (uint8_t *)&response_buff[response_index_o][0], resp_size[response_index_o]);
        response_index_o++;
        if (response_index_o == DAP_PACKET_COUNT) {
            response_index_o = 0U;
        }
        response_count_o++;
    } else {
        response_idle = 1U;
    }
}

/**
 * @brief 初始化状态量
 *
 */
static void APP_DAP_StateInit(void) {
    // Initialize variables
    request_index_i = 0U;
    request_index_o = 0U;
    request_count_i = 0U;
    request_count_o = 0U;
    request_idle = 1U;
    response_index_i = 0U;
    response_index_o = 0U;
    response_count_i = 0U;
    response_count_o = 0U;
    response_idle = 1U;
}

/**
 * @brief 初始化DAP
 *
 */
void APP_DAP_Init(void) {
    DAP_GetSerialNumber();
    DAP_Setup();
    APP_DAP_StateInit();
}

/**
 * @brief DAP包处理
 *
 */
void APP_DAP_Handle(void) {
    uint32_t n;

    /* Process pending requests */
    while (request_count_i != request_count_o) {
        /* Handle Queue Commands */
        n = request_index_o;
        while (request_buff[n][0] == ID_DAP_QueueCommands) {
            request_buff[n][0] = ID_DAP_ExecuteCommands;
            n++;
            if (n == DAP_PACKET_COUNT) {
                n = 0U;
            }
            if (n == request_index_i) {
                // flags = osThreadFlagsWait(0x81U, osFlagsWaitAny,
                // osWaitForever); if (flags & 0x80U) {
                //     break;
                // }
            }
        }

        /* Execute DAP Command */
        resp_size[response_index_i] = (uint16_t)DAP_ExecuteCommand((uint8_t *)&request_buff[request_index_o][0], (uint8_t *)&response_buff[response_index_i][0]);

        /* Update Request Index and Count */
        request_index_o++;
        if (request_index_o == DAP_PACKET_COUNT) {
            request_index_o = 0U;
        }
        request_count_o++;

        if (request_idle) {
            if ((uint16_t)(request_count_i - request_count_o) != DAP_PACKET_COUNT) {
                request_idle = 0U;
                usbd_ep_start_read(0, DAP_OUT_EP, (uint8_t *)&request_buff[request_index_i][0], DAP_PACKET_SIZE);
            }
        }

        response_index_i++;
        if (response_index_i == DAP_PACKET_COUNT) {
            response_index_i = 0U;
        }
        response_count_i++;

        if (response_idle) {
            if (response_count_i != response_count_o) {
                // Load data from response buffer to be sent back
                n = response_index_o++;
                if (response_index_o == DAP_PACKET_COUNT) {
                    response_index_o = 0U;
                }
                response_count_o++;
                response_idle = 0U;
                usbd_ep_start_write(0, DAP_IN_EP, (uint8_t *)&response_buff[n][0], resp_size[n]);
            }
        }
    }
}
